International Student Immigration Risk Analysis

Introduction

This analysis examines international student data to assess immigration risks and department-level exposure. The goal is to:

  1. Understand the distribution of international students by country and department
  2. Create an immigration risk index based on travel advisories and visa issuance trends
  3. Identify departments with high exposure to immigration risks
  4. Visualize the findings through interactive maps and charts

Student Distribution Analysis

Let’s analyze the distribution of international students by country and department.

Code
# Show top countries overall with labeled bars
students_by_country %>%
  top_n(10, StudentCount) %>%
  ggplot(aes(x = reorder(Country, StudentCount), y = StudentCount)) +
  geom_col(fill = "#005A43") +
  geom_text(aes(label = StudentCount), hjust = -0.3, color = "#005A43", fontface = "bold") +
  coord_flip() +
  scale_y_continuous(limits = c(0, max(students_by_country$StudentCount[1:10]) * 1.1)) +
  labs(
    title = "Top 10 Countries of Origin for International Graduate Students",
    x = "Country",
    y = "Number of Students"
  )

Code
# Show top departments by international student count with labeled bars
students_by_dept %>%
  top_n(10, StudentCount) %>%
  ggplot(aes(x = reorder(Department, StudentCount), y = StudentCount)) +
  geom_col(fill = "#6CC24A") +
  geom_text(aes(label = StudentCount), hjust = -0.3, color = "#005A43", fontface = "bold") +
  coord_flip() +
  scale_y_continuous(limits = c(0, max(students_by_dept$StudentCount[1:10]) * 1.1)) +
  labs(
    title = "Top 10 Departments by International Graduate Student Enrollment",
    x = "Department",
    y = "Number of Students"
  )

Immigration Risk Index Development

We’ll now create an immigration risk index based on: 1. Travel advisory level (higher level = higher risk) 2. Recent trends in student visa issuances (declining = higher risk) 3. Volatility in visa issuances (higher volatility = higher risk)

Immigration Risk Index Methodology

The Immigration Risk Index combines three key factors into a comprehensive score ranging from 1 to 5. The mathematical formula is:

\[\text{Risk Score} = 1 + 4 \times [0.5 \times \text{Advisory Score} + 0.3 \times \text{Trend Score} + 0.2 \times \text{Volatility Score}]\]

Where:

  1. Advisory Score = \(\frac{\text{Travel Advisory Level} - 1}{3}\)
    • Based on U.S. State Department travel advisory levels (1-4)
    • Level 1 = 0.00, Level 2 = 0.33, Level 3 = 0.67, Level 4 = 1.00
  2. Trend Score = \(\frac{\text{Normalized Visa Trend} + 1}{2}\)
    • Where Normalized Visa Trend = \(\frac{\text{12-month visa issuance slope}}{\text{Average monthly visas}}\)
    • Measures the trajectory of student visa issuances (increasing or decreasing)
    • Values range from 0 (strongly increasing) to 1 (strongly decreasing)
  3. Volatility Score = \(\frac{\text{CV of visa issuances}}{\text{95th percentile CV}}\)
    • Where CV (Coefficient of Variation) = \(\frac{\text{Standard Deviation}}{\text{Mean}}\)
    • Measures the unpredictability in visa issuance patterns
    • Higher values indicate greater uncertainty

The final score is mapped to risk categories: - Low Risk: 1.0 - 2.0 - Moderate Risk: 2.0 - 3.0 - High Risk: 3.0 - 4.0 - Very High Risk: 4.0 - 5.0

Code
# View the risk index - filtered to only countries with students
risk_index %>%
  # Join with student data to only keep countries with students
  inner_join(students_by_country, by = "Country") %>%
  select(Country, risk_score, risk_category, StudentCount, advisory_score, trend_score, volatility_score) %>%
  arrange(desc(risk_score)) %>%
  mutate(risk_score = round(risk_score, 1)) %>%
  DT::datatable(
    caption = "Immigration Risk Index by Country (Only Countries with Current Students)",
    options = list(pageLength = 20, searchHighlight = TRUE)
  ) %>%
  formatStyle(
    'risk_category',
    backgroundColor = styleEqual(
      c("Low", "Moderate", "High", "Very High"), 
      c('#6CC24A', '#a5d168', '#f7c143', '#d32424')
    )
  ) %>%
  formatStyle(
    'risk_score',
    background = styleColorBar(c(1,5), '#005A43'),
    backgroundSize = '100% 90%',
    backgroundRepeat = 'no-repeat',
    backgroundPosition = 'center',
    color = 'white'
  ) %>%
  formatRound(c('advisory_score', 'trend_score', 'volatility_score'), digits = 2)

Department Exposure Analysis

Now we’ll analyze which departments have the highest exposure to immigration risks.

Department Risk Exposure

We calculate a Department Exposure Index using the formula:

\[\text{Exposure Index} = \frac{\text{Average Risk Score} \times \text{Number of Students}}{10}\]

This index identifies departments that are most vulnerable to immigration policy changes, combining both the risk level and the number of affected students.

Code
# Create a more distinctive color palette for the high-risk departments
distinct_risk_palette <- colorRampPalette(c("#005A43", "#6CC24A", "#f7c143", "#d32424"))(10)

# Improve the high-risk departments visualization 
department_exposure %>%
  filter(TotalStudents >= 5) %>%  # Focus on departments with at least 5 int'l students
  top_n(10, HighRiskStudents) %>%  # Show top 10 departments
  ggplot(aes(x = reorder(Department, HighRiskStudents), 
             y = HighRiskStudents,
             fill = AverageRisk)) +
  geom_col() +
  geom_text(aes(label = HighRiskStudents), 
            hjust = -0.3, 
            color = "#005A43", 
            fontface = "bold") +
  scale_fill_gradientn(colors = distinct_risk_palette, 
                       name = "Average\nRisk Score") +
  coord_flip() +
  scale_y_continuous(limits = c(0, max(department_exposure$HighRiskStudents) * 1.2)) +
  labs(
    title = "Departments with Most High-Risk Students",
    subtitle = "Number of students from high-risk countries (risk score ≥ 3.5)",
    x = "",
    y = "Number of High-Risk Students"
  ) +
  theme(
    panel.grid.major.y = element_blank(),
    axis.text.y = element_text(color = "#005A43")
  )

Interactive Department Risk Map

This interactive visualization maps departments by their total international student population and average risk level. Larger bubbles indicate more students, while colors represent different risk levels.

Code
# Create a more distinctive color palette for the interactive plot
# Using our institution colors but with more variation for better differentiation
interactive_palette <- colorRampPalette(c("#00843D", "#6CC24A", "#FFDE17", "#E87722", "#9A3324"))(100)

# Create a more informative interactive plot
p <- plot_ly(
  data = department_exposure %>% filter(TotalStudents >= 3),
  x = ~TotalStudents,
  y = ~AverageRisk,
  size = ~TotalStudents,
  color = ~AverageRisk,
  colors = interactive_palette,
  text = ~paste0(
    Department, "<br>",
    "Total Students: ", TotalStudents, "<br>",
    "Average Risk: ", round(AverageRisk, 1), "<br>",
    "High-Risk Students: ", HighRiskStudents, " (", 
    scales::percent(HighRiskProportion, accuracy = 1), ")"
  ),
  hoverinfo = "text",
  type = "scatter",
  mode = "markers"
) %>%
  layout(
    title = "Department Immigration Risk Analysis",
    xaxis = list(
      title = "Total International Students",
      gridcolor = "#f0f0f0"
    ),
    yaxis = list(
      title = "Average Risk Score (1-5)",
      gridcolor = "#f0f0f0",
      range = c(1, 5)
    ),
    shapes = list(
      # Line for Low/Moderate boundary
      list(
        type = "line", 
        x0 = 0, x1 = max(department_exposure$TotalStudents) * 1.1,
        y0 = 2, y1 = 2,
        line = list(color = "#6CC24A", dash = "dash", width = 1)
      ),
      # Line for Moderate/High boundary
      list(
        type = "line", 
        x0 = 0, x1 = max(department_exposure$TotalStudents) * 1.1,
        y0 = 3, y1 = 3,
        line = list(color = "#f7c143", dash = "dash", width = 1)
      ),
      # Line for High/Very High boundary
      list(
        type = "line", 
        x0 = 0, x1 = max(department_exposure$TotalStudents) * 1.1,
        y0 = 4, y1 = 4,
        line = list(color = "#d32424", dash = "dash", width = 1)
      )
    ),
    annotations = list(
      # Label for Low Risk
      list(x = max(department_exposure$TotalStudents) * 0.9, y = 1.5, 
           text = "Low Risk", showarrow = FALSE, 
           font = list(color = "#6CC24A", size = 12)),
      # Label for Moderate Risk
      list(x = max(department_exposure$TotalStudents) * 0.9, y = 2.5, 
           text = "Moderate Risk", showarrow = FALSE, 
           font = list(color = "#a5d168", size = 12)),
      # Label for High Risk
      list(x = max(department_exposure$TotalStudents) * 0.9, y = 3.5, 
           text = "High Risk", showarrow = FALSE, 
           font = list(color = "#f7c143", size = 12)),
      # Label for Very High Risk
      list(x = max(department_exposure$TotalStudents) * 0.9, y = 4.5, 
           text = "Very High Risk", showarrow = FALSE, 
           font = list(color = "#d32424", size = 12, font = list(weight = "bold")))
    )
  )

# Display the interactive plot
p
Code
# Create interactive table of department exposure
department_exposure %>%
  select(Department, TotalStudents, AverageRisk, HighRiskStudents, 
         HighRiskProportion, ExposureIndex) %>%
  mutate(
    AverageRisk = round(AverageRisk, 1),
    HighRiskProportion = scales::percent(HighRiskProportion, accuracy = 0.1),
    ExposureIndex = round(ExposureIndex, 1)
  ) %>%
  arrange(desc(ExposureIndex)) %>%
  DT::datatable(
    caption = "Department Exposure to Immigration Risks",
    options = list(pageLength = 20, searchHighlight = TRUE)
  ) %>%
  formatStyle(
    'AverageRisk',
    backgroundColor = styleInterval(
      c(2, 3, 4), 
      c('#6CC24A', '#a5d168', '#f7c143', '#d32424')
    )
  )

Interactive Map of Student Origins and Risks

This map visualizes where international students come from and the immigration risk level for each country.

Code
# Create color palette for risk categories - ensuring exact match with categories
risk_colors <- colorFactor(
  palette = c("#6CC24A", "#a5d168", "#f7c143", "#d32424"),
  domain = c("Low", "Moderate", "High", "Very High"),
  ordered = TRUE
)

# Initialize the leaflet map with base tiles
leaflet_map <- leaflet() %>%
  addProviderTiles(providers$CartoDB.Positron) %>%  # Use a more subtle base map
  setView(lng = 0, lat = 30, zoom = 2)

# Add circle markers for countries with students, sized by count and colored by risk
leaflet_map <- leaflet_map %>%
  addCircleMarkers(
    data = world_data %>% filter(!is.na(StudentCount)),
    lng = ~sf::st_coordinates(sf::st_centroid(geometry))[,1],
    lat = ~sf::st_coordinates(sf::st_centroid(geometry))[,2],
    radius = ~sqrt(StudentCount) * 3,
    fillColor = ~risk_colors(risk_category),  # This ensures colors match categories
    color = "#005A43",  # Border color using institutional color
    weight = 1.5,
    opacity = 1,
    fillOpacity = 0.8,
    label = ~paste0(admin, ": ", StudentCount, " students (Risk: ", risk_category, ")"),
    popup = ~paste0(
      "<strong style='color:#005A43;'>", admin, "</strong><br>",
      "<b>Students:</b> ", StudentCount, "<br>",
      "<b>Risk Category:</b> ", risk_category, "<br>",
      "<b>Risk Score:</b> ", round(risk_score, 1), "<br>"
    )
  )

# Add legend with exact same colors as the markers
leaflet_map <- leaflet_map %>%
  addLegend(
    position = "bottomright",
    colors = c("#6CC24A", "#a5d168", "#f7c143", "#d32424"),
    labels = c("Low Risk", "Moderate Risk", "High Risk", "Very High Risk"),
    title = "<span style='color:#005A43;'><b>Immigration Risk Level</b></span>",
    opacity = 0.9
  )

# Display the map
leaflet_map

Conclusion and Recommendations

Based on our analysis, we can make the following observations and recommendations:

  1. Countries of Concern:
    • Countries with high risk scores should be closely monitored for policy changes
    • Special attention should be paid to countries with both high risk scores and large student populations
  2. Departments at Risk:
    • Departments with high exposure indices should develop contingency plans
    • Consider diversifying international student recruitment to reduce concentration risk
    • Provide additional support resources for students from high-risk countries
  3. Ongoing Monitoring:
    • Regularly update the risk index with new travel advisory and visa issuance data
    • Track visa approval rates and processing times for early warning signs
    • Maintain close communication with international student offices and embassy contacts
  4. Risk Mitigation Strategies:
    • Develop online learning options as contingency for visa disruptions
    • Create exchange programs with universities in lower-risk countries
    • Establish emergency funding for students facing unexpected visa issues
    • Provide specialized immigration legal support for students from high-risk countries

This analysis provides a data-driven foundation for strategic planning to support international students and minimize disruption to academic departments from potential visa issues.